#lang racket
(require "web-builder.rkt" (only-in "../Global/package.rkt" define-class))
(require "../lock/package.rkt")
(provide build-universe create-new-player)
;; A Universe is a (create-universe Web)
;; A World is a (create-world Node Lock-Code)
;; Lock-Code is code that would evaluate to a Lock, unquoted
;; a Player is a (new player% [Listof code] World)

(define-lock (base-lock n) #t)

;; -> Universe
;; makes a new universe
(define (build-universe)
  (create-universe (make-web)))

;; create-universe : Web -> Universe
;; creates a universe
(define (create-universe web)
  ;;TODO
  (define worlds (append-map (λ (w) (map create-world w)) web))
  (new universe% [world worlds] [nodes web]))

;; universe%
(define-class universe%
  ;; The list of worlds for this universe
  ;; The web
  (init world nodes)
  ;; SETUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (define worlds world)
  (define web nodes)
  (define begining (node-parent (findf (λ (n) (= 0 (node-id n))) (first web)))); 0,0 
  ;; All The players
  (define players empty)
  
  ;; ACCESSORS ;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; Id -> [Or Player #f]
  ;; gets that player
  (define (get-player-by-id id)
    (findf (λ (p) (= id (send p get-id))) players))
  
  ;; MUTATORS ;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; Player World -> Void
  ;; Moves player
  (define (move-player p w)
    (send (send p get-loc) player-leave p)
    (send p move-to w)
    (send w player-join p))
  
  ;; Player -> Void
  ;; Injects this player into 0,0
  (define (inject player)
    (send begining player-join player)
    (send player move-to begining))
  
  ;; Player -> Void
  ;; removes this player from the universe
  (define (kill p)
    (send (send p get-loc) player-leave p)
    (set! players (remq p players)))
  )
  
;; create-world : Node -> World
;; creates a world
(define (create-world node)
  (new world% [node-base node] [lock-base base-lock]))

;; world%
(define-class world%
  ;; Node and Lock
  (init node-base lock-base)
  ;; SETUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  (define lock lock-base)
  ;; Node, Makes this the nodes parent
  (define node (let () (set-node-parent! node-base this) node-base))
  ;; [Listof Players]
  (define players empty)
  
  ;; OTHER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; Any -> Any
  ;; Attempts to pick the lock to this world
  (define/pubic (pick key)
    (lock key))
  
  ;; ACCESSORS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; -> [Listof World]
  (define/public (get-links)
    (map node-parent (node-links node)))
  
  ;; -> [Listof Players]
  (define/public (get-players)
    players)
  
  ;; MUTATORS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; Player -> Void
  ;; Adds this player to this world
  (define/public (player-join player)
    (set! players (cons player players)))
  
  ;; Player -> Void
  ;; Removes any players eq to this one
  (define/public (player-leave player)
    (set! player (remq player players)))
  )
  
;; Id -> Player
;; creates a new player with that id
(define (create-new-player id)
  (new player% [code '()] [location (void)] [id id]))

;; player%
(define-class player%
  ;; code: sexpr of the eval space for a player
  ;; location: a world%
  (init code location id)
  
  ;; SETUP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; a Sexpr
  (define env code)
  ;; a World
  (define loc location)
  ;; Id
  (define ident id)
  
  ;; Accessors ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; -> Sexpr
  (define/public (get-env) env)
  
  ;; -> World
  (define/public (get-loc) loc)
  
  ;; -> Id
  (define/public (get-id) ident)
  
  ;; Mutators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  
  ;; World -> Void
  ;; Moves this player to that world
  (define/public (move-to world)
    (set! loc world))
  
  )
  